/*
 * InputLeap -- mouse and keyboard sharing utility
 * Copyright (C) 2012-2016 Symless Ltd.
 * Copyright (C) 2002 Chris Schoeneman
 *
 * This package is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * found in the file LICENSE that should have accompanied this file.
 *
 * This package is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#pragma once

#include <ws2tcpip.h>
// declare no functions in winsock2
#ifndef INCL_WINSOCK_API_PROTOTYPES
#define INCL_WINSOCK_API_PROTOTYPES 0
#endif
#define INCL_WINSOCK_API_TYPEDEFS 0

#include "arch/IArchNetwork.h"
#include "arch/IArchMultithread.h"

#include <WinSock2.h>
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>

#include <mutex>
#include <list>

#pragma comment(lib, "ws2_32.lib")

#define ARCH_NETWORK ArchNetworkWinsock

namespace inputleap {

class ArchSocketImpl {
public:
    SOCKET m_socket;
    int m_refCount;
    WSAEVENT m_event;
    bool m_pollWrite;
};

class ArchNetAddressImpl {
public:
    static ArchNetAddressImpl* alloc(size_t);

public:
    int m_len;
    struct sockaddr_storage m_addr;
};
#define ADDR_HDR_SIZE    offsetof(ArchNetAddressImpl, m_addr)
#define TYPED_ADDR(type_, addr_) (reinterpret_cast<type_*>(&addr_->m_addr))

//! Win32 implementation of IArchNetwork
class ArchNetworkWinsock : public IArchNetwork {
public:
    ArchNetworkWinsock();
    virtual ~ArchNetworkWinsock();

    virtual void init();

    // IArchNetwork overrides
    virtual ArchSocket newSocket(EAddressFamily, ESocketType);
    virtual ArchSocket copySocket(ArchSocket s);
    virtual void closeSocket(ArchSocket s);
    virtual void closeSocketForRead(ArchSocket s);
    virtual void closeSocketForWrite(ArchSocket s);
    virtual void bindSocket(ArchSocket s, ArchNetAddress addr);
    virtual void listenOnSocket(ArchSocket s);
    virtual ArchSocket acceptSocket(ArchSocket s, ArchNetAddress* addr);
    virtual bool connectSocket(ArchSocket s, ArchNetAddress name);
    virtual int pollSocket(PollEntry[], int num, double timeout);
    virtual void unblockPollSocket(ArchThread thread);
    virtual size_t readSocket(ArchSocket s, void* buf, size_t len);
    virtual size_t writeSocket(ArchSocket s,
                            const void* buf, size_t len);
    virtual void throwErrorOnSocket(ArchSocket);
    virtual bool setNoDelayOnSocket(ArchSocket, bool noDelay);
    virtual bool setReuseAddrOnSocket(ArchSocket, bool reuse);
    virtual std::string getHostName();
    virtual ArchNetAddress newAnyAddr(EAddressFamily);
    virtual ArchNetAddress copyAddr(ArchNetAddress);
    virtual ArchNetAddress nameToAddr(const std::string&);
    virtual void closeAddr(ArchNetAddress);
    virtual std::string addrToName(ArchNetAddress);
    virtual std::string addrToString(ArchNetAddress);
    virtual EAddressFamily getAddrFamily(ArchNetAddress);
    virtual void setAddrPort(ArchNetAddress, int port);
    virtual int getAddrPort(ArchNetAddress);
    virtual bool isAnyAddr(ArchNetAddress);
    virtual bool isEqualAddr(ArchNetAddress, ArchNetAddress);

private:
    void initModule(HMODULE);

    void setBlockingOnSocket(SOCKET, bool blocking);

    void throwError(int);
    void throwNameError(int);

private:
    typedef std::list<WSAEVENT> EventList;

    std::mutex mutex_;
    EventList m_unblockEvents;
};

} // namespace inputleap
